﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Threading.Tasks;


namespace Dwarf.Managed
{
    /// <summary>
    /// Solve the Combinational Logic problem with TPL parallelization.
    /// </summary>
    /// <remarks> 
    /// For the demonstration purposes of the project, 
    /// we have chosen the algorithm that counts the number of “1” bits in a bit string.
    /// </remarks>
    class Solver
    {
        /// <summary>
        /// Reference to file.
        /// </summary>
        public BinaryReader input;

        /// <summary>
        /// Length of file
        /// </summary>
        public int length;

        /// <summary>
        /// Result counter
        /// </summary>
        public long resultCount = 0;

        /// <summary>
        /// Size of block for each process
        /// </summary>
        public int bufferSize = 0;

        /// <summary>
        /// Empty constructor.
        /// </summary>
        public Solver()
        {
        }

        /// <summary>
        /// TPL based method.
        /// </summary>
        public void Solve()
        {
            ulong[] buffer = new ulong[bufferSize];

            int len;

            //Loop while not End Of File.
            while (input.BaseStream.Position < length)
            {
                len = Math.Min((int )(length - input.BaseStream.Position) / 8, bufferSize);

                //Read block of data from file.
                for (int i = 0; i < len; i++)
                {                    
                    buffer[i] = input.ReadUInt64();
                }

                if (len < bufferSize)
                { 
                    ulong lastCount = 0;

                    int numBytes = (int)(length - input.BaseStream.Position);

                    for (byte i = 0; i < numBytes; i++)
                    {
                        lastCount += (ulong)(input.ReadByte() << i * 8);
                    }

                    buffer[len] = lastCount;

                    len++;
                }                

                 //Loop for length of block.
                //since the iterations of the loop are independent by data, 
                //each iteration may be performed as separate thread.

                Parallel.For(0, len, (i) =>
                {
                    ulong tls = 0;
                    //This line of code is used for decrease of input file size.
                    //Such modification would reduce the input file size, 
                    //but the algorithm is still belongs dwarf.
                    //Also this line does not affect the result.
                    for (int k = 0; k < 1000000; ++k)
                    {
                        //Calculate number of “1” in current uint64.
                        tls = buffer[i];
                        tls -= tls >> 1 & 0x5555555555555555;
                        tls = (tls & 0x3333333333333333) + ((tls >> 2) & 0x3333333333333333);
                        tls = (tls + (tls >> 4)) & 0x0f0f0f0f0f0f0f0f;
                    }
                    Interlocked.Add(ref resultCount, (long)(tls * 0x0101010101010101 >> 56));
                }
                );
            }
        }


    }
}
